package com.zp.self.module.level_4_算法练习.算法.深度优先搜索.回溯;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author By ZengPeng
 */
public class 力扣_39_组合总和 {
    //测试
    public static void main(String[] args) {
        System.out.println(new 力扣_39_组合总和().combinationSum(new int[]{2,3,6,7},7));
        System.out.println(new 力扣_39_组合总和().combinationSum(new int[]{2,3,5},8));
        System.out.println(new 力扣_39_组合总和().combinationSum(new int[]{2},1));
    }

    /**
    题目：给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ，
     找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ，
     并以列表形式返回。你可以按 任意顺序 返回这些组合。
     candidates 中的 同一个 数字可以 无限制重复被选取 。
     如果至少一个数字的被选数量不同，则两种组合是不同的。 
     对于给定的输入，保证和为 target 的不同组合数少于 150 个。

     示例 1：
     输入：candidates = [2,3,6,7], target = 7
     输出：[[2,2,3],[7]]
     解释：
     2 和 3 可以形成一组候选，2 + 2 + 3 = 7 。注意 2 可以使用多次。
     7 也是一个候选， 7 = 7 。
     仅有这两种组合。

     示例 2：
     输入: candidates = [2,3,5], target = 8
     输出: [[2,2,2,2],[2,3,3],[3,5]]

     示例 3：
     输入: candidates = [2], target = 1
     输出: []

    分析：【perfect】
        1.回溯法 = 递归 + :
             1.单步操作：判断当前值 与 target的大小比较,如果较小继续递归
             2.终止条件：当前值>target || 当前==target
             3.返回值：判断返回值是否为null
        优化，前面的路，可以用 双队列 记录，由于List,不用开辟过多内存【38_外观树自己才用过，又忘记了-.-】

    边界值 & 注意点：
       1.重复了。解：排序 + 记录当前值，只能从当前值及后面的值中取
    **/
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        Arrays.sort(candidates);
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < candidates.length; i++) {//这里有问题啊  刚好等于此值
            List<Integer> parent = new ArrayList<>();
            parent.add(candidates[i]);
            List<List<Integer>> recursive = recursive(candidates, target - candidates[i], parent, i);
            if(recursive!=null)
                res.addAll(recursive);
        }
        return res;
    }

    /**
    *  递归求解
    * @ candidates 数组
    * @ target 目标值剩余大小
    * @ parent 前面已经存放的值
    * @ index  不能再向小的坐标拿值了，不然会重复
    **/
    public  List<List<Integer>> recursive(int[] candidates, int target, List<Integer> parent, int index){
        if(target<0) return null;
        List<List<Integer>> res = new ArrayList<>();
        if(target>0){
            for (int i = index; i < candidates.length; i++) {//这里有问题啊  刚好等于此值
                List<Integer> myself = new ArrayList<>(parent);
                myself.add(candidates[i]);
                List<List<Integer>> childs = recursive(candidates, target - candidates[i], myself, i);
                if(childs!=null)
                    res.addAll(childs);
            }
        }else {
            res.add(parent);
        }
        return res;
    }
}
